import filecmp
import os

import table_ast
import table_lexer
import table_parser

def to_lua_files(indir, outdir):
    def check_replace_file(filename):
        if not os.path.exists(filename[:-1]) or \
           not filecmp.cmp(filename, filename[:-1], False):
            os.replace(filename, filename[:-1])
        else:
            os.remove(filename)

    def parse_ast(infile):
        lexer = table_lexer.TableLexer()
        lexer.parse(infile)
        parser = table_parser.TableParser()
        return parser.parse(lexer)

    def parse_blanks(infile):
        blanks = []
        with open(infile, 'r', encoding='utf8') as fo:
            for i, line in enumerate(fo.readlines()):
                if line.isspace():
                    blanks.append(i + 1)
        return blanks

    def generate_lua_file(indir, outdir, filename):
        filepart = os.path.splitext(filename)[0]
        outpathpart = os.path.join(outdir, filepart)
        infile = os.path.abspath(os.path.join(indir, filename))
        print('lua files %s ...' % infile)
        root, blanks = parse_ast(infile), parse_blanks(infile)
        outfile, prefix = outpathpart+'.lua~', ''
        GeneratorLua(blanks).Generate(root, outfile, prefix)
        check_replace_file(outfile)

    if os.path.exists(indir):
        if not os.path.exists(outdir):
            os.mkdir(outdir)
        for filename in os.listdir(indir):
            generate_lua_file(indir, outdir, filename)

class GeneratorLua:
    def __init__(self, blanks):
        self.blanks, self.i = blanks, 0
        self.lineno = 0

    def Generate(self, root, outfile, prefix = ''):
        with open(outfile, 'w', encoding='utf8') as self.fo:
            self.fo.write(prefix)
            for entity in root.externalDeclarations:
                self.__generateentity(entity, 0)

    def __generateentity(self, entity, level):
        if isinstance(entity, table_ast.EnumDefinition):
            return self.__generateenumblock(entity, level)

        if isinstance(entity, table_ast.EnumDeclaration):
            return self.__generateenummember(entity, level)

        if isinstance(entity, table_ast.Comment):
            return self.__writeline2file(level,
                entity.text.value.rstrip().replace('//', '--', 1),
                entity.text.lineno, canInline = True, strSpacer = '  ')

    def __generateenumblock(self, entity, level):
        self.idList, self.valList = [], []
        self.nsVal, self.iVal = '', -1
        self.wpos = None
        self.__writeline2file(level, '-- %s' % entity.name.value,
            entity.name.lineno)
        self.__generateblockmember(entity, level)

    def __generateblockmember(self, entity, level):
        self.__writeline2file(level, '-- {', entity.name.lineno)
        for member in entity.declarationList.declarationList:
            self.__generateentity(member, level + 1)
        self.__writeline2file(level, '-- }')

    def __generateenummember(self, member, level):
        self.__writeenummember(member.memName.value,
            self.__enumValue2formatStr(member.memValue), level,
            member.memName.lineno)

    def __writeenummember(self, idStr, valStr, level, lineno):
        if self.lineno != lineno:
            self.wpos = self.fo.tell()
            del self.idList[:], self.valList[:]
        self.fo.seek(self.wpos)
        self.idList.append(idStr), self.valList.append(valStr)
        self.__writeline2file(level,
            '%s = %s' % (', '.join(self.idList), ', '.join(self.valList)),
            lineno)

    def __writeline2file(self, level, linedata, lineno = -1, **kwargs):
        while len(self.blanks) > self.i and self.blanks[self.i] < lineno:
            self.fo.write('\n')
            self.i += 1
        if kwargs and kwargs['canInline'] and self.lineno == lineno:
            self.fo.seek(self.fo.tell() - len(os.linesep))
            self.fo.write(kwargs['strSpacer'])
        elif level != 0:
            self.fo.write('\t' * level)
        self.fo.write(linedata)
        self.fo.write('\n')
        if lineno != -1:
            self.lineno = lineno

    def __enumValue2formatStr(self, node):
        if isinstance(node, table_ast.NsIdList):
            self.nsVal, self.iVal = self.__to_lua_enum_value(node), 0
        elif node:
            self.nsVal, self.iVal = '', int(node)
        else:
            self.iVal += 1
        if self.nsVal and self.iVal != 0:
            return '%s+%d' % (self.nsVal, self.iVal)
        elif self.nsVal:
            return self.nsVal
        else:
            return str(self.iVal)

    @staticmethod
    def __to_lua_enum_value(node):
        return node.idList[-1].value
